﻿//Copyright (c) Microsoft Corporation
//
//All rights reserved. 
//
//Licensed under the Apache License, Version 2.0 (the ""License""); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.

using FluentAssertions;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TED.WindowsAzure.Unplugged.Mobile.RemoteStorage.AzureBlob.Storage;
using TED.WindowsAzure.Unplugged.Mobile.Sync.Events;
using TED.WindowsAzure.Unplugged.Mobile.Sync.File;
using TED.WindowsAzure.Unplugged.Mobile.Sync.Folder;
using TED.WindowsAzure.Unplugged.Mobile.Tests.Helpers;

namespace TED.WindowsAzure.Unplugged.Mobile.Tests.IntegrationTests
{
    [TestClass]
    public class IntegrationTests2 : IntegrationTestBase
    {
        [TestInitialize]
        public void Setup()
        {
        }

        [TestCleanup]
        public async Task TearDown()
        {
            // Clean up blob storage
            await CloudBlobHelper.CleanUpBlob();

            // Clean up local FS
            await BinaryStorageHelper.CleanUp();
        }

        [TestMethod]
        public async Task ReadAsync_BasicInitialization()
        {

            //Arrange
            await CloudBlobHelper.FillBlobStorage(3);

            //Act
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await client.SyncContext.GetSyncFolderAsync(FolderName);
            await folder.ReadAsync();
            await AssertLocalFiles(0);
            await folder.PullAsync();

            //Assert
            await AssertLocalFiles(3);
        }

        [TestMethod]
        public async Task CreateNewFile_Cancel_NoLocalFilesOnDisk()
        {
            //Arrange
            //Act
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            Assert.AreEqual(0, TableHelper.GetCount());

            ISyncFolder folder = await client.SyncContext.GetSyncFolderAsync(FolderName);

            Assert.AreEqual(0, TableHelper.GetCount());

            ISyncFile file = await folder.CreateFileAsync("myfile.txt");

            using (var writer = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                writer.WriteLine("foo");
                writer.Flush();
            }
            await folder.CancelAsync();

            // Assert
            Assert.AreEqual(0, TableHelper.GetCount());

            Assert.AreEqual(1,
                await BinaryStorageHelper.GetCountAsync(FolderName));
        }

        [TestMethod]
        public async Task Pull_Truncate_NoLocalFiles()
        {

            await CloudBlobHelper.FillBlobStorage(2);

            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));
            var container = await client.SyncContext
                .GetSyncFolderAsync(FolderName);

            await container.PullAsync();

            var file = await container.CreateFileAsync("2.txt");
            using (var s = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                s.Write(Ipsum.GetPhrase(20));
            }
            await file.CommitAsync();

            await container.PushAsync();

            // Assert
            Assert.AreEqual(3, (await CloudBlobHelper.GetBlobCount()));
            await AssertLocalFiles(3);

            await container.TruncateAsync();
            Assert.AreEqual(3, (await CloudBlobHelper.GetBlobCount()));
            await AssertLocalFiles(0);
        }

        [TestMethod]
        public async Task CreateNewFile_ZeroSize()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            Assert.AreEqual(0, TableHelper.GetCount());

            ISyncFolder folder = await client.SyncContext.GetSyncFolderAsync(FolderName);

            Assert.AreEqual(0, TableHelper.GetCount());

            ISyncFile file = await folder.CreateFileAsync("myfile.txt");

            //Write Zero length file
            using (var s = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                s.Write("");
            }
            await file.CommitAsync();

            // Assert
            Assert.AreEqual(1, TableHelper.GetCount());

            var dbFile = TableHelper.GetItems().First();
            Assert.AreEqual(SyncFileStatus.Created, dbFile.Status);
            Assert.AreEqual("myfile.txt", dbFile.FileName);

            Assert.AreEqual(1,
                await BinaryStorageHelper.GetCountAsync(FolderName));

            var localStorageFile =
              await BinaryStorageHelper.GetFileAsync(string.Format("{0}\\{1}", dbFile.FolderName, dbFile.FileName));
            Assert.IsNotNull(localStorageFile);
        }

        [TestMethod]
        public async Task CreateNewContainer_InvalidName()
        {
            //Arrange
            string containerName = "container-This is a terrible Container&Name.pdf";

            // Act
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            var folder = await client.SyncContext.GetSyncFolderAsync(containerName);

            int pushStarted = 0, pushCompleted = 0, pushProgress = 0, syncListBuilt = 0;

            folder.PushStarted += (s, e) =>
            {
                pushStarted++;
            };
            folder.PushCompleted += (s, e) =>
            {
                pushCompleted++;
            };
            folder.PushProgressChanged += (s, e) =>
            {
                pushProgress++;
                Assert.AreEqual(SyncFileResult.Failure, e.Result);
                Assert.IsNull(e.RemoteFile);
                Assert.IsNotNull(e.LocalFile);
            };
            folder.SyncListBuilt += (s, e) =>
            {
                syncListBuilt++;
                Assert.AreEqual(1, e.SyncFiles.Count());
            };

            ISyncFile file = await folder.CreateFileAsync("newfile.dat");
            using (var s = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                s.Write(Ipsum.GetPhrase(20));
            }

            await file.CommitAsync();

            await folder.PushAsync();
        }

        [TestMethod]
        public async Task CreateNewFile_WriteMultipleTimesToStream()
        {
            await CloudBlobHelper.FillBlobStorage(2);

            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));
            var folder = await client.SyncContext
                .GetSyncFolderAsync(FolderName);

            await folder.PullAsync();
            await AssertLocalFiles(2);
            var file = await folder.CreateFileAsync("2.txt");
            string phrase1 = Ipsum.GetPhrase(20);
            string phrase2 = Ipsum.GetPhrase(40);

            using (var s = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                s.Write(phrase1);
            }
            await file.CommitAsync();

            await folder.PushAsync();

            // Assert
            Assert.AreEqual(3, (await CloudBlobHelper.GetBlobCount()));
            await AssertLocalFiles(3);
        }

        [TestMethod]
        public async Task PushWithNoCommit_NoFileUploaded()
        {
            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));
            var container = await client.SyncContext.GetSyncFolderAsync(this.FolderName);

            var file = await container.CreateFileAsync("2.txt");

            using (var s = new StreamWriter((await file.OpenWriteAsync()).AsStreamForWrite()))
            {
                s.Write(Ipsum.GetPhrase(20));
            }

            //No Commit
            await container.PullAsync();

            await container.PushAsync();

            // Assert
            Assert.AreEqual(0, (await CloudBlobHelper.GetBlobCount()));

            Assert.AreEqual(1, await BinaryStorageHelper.GetCountAsync(FolderName));
            Assert.AreEqual(1, TableHelper.GetCount());
        }

        [TestMethod]
        public async Task TwoFolderPull_DifferentRemoteFilesForEach()
        {

            var client = new UnpluggedMobileServiceClient(this.MobileServicesUri, this.MobileServicesKey);

            await client.SyncContext.InitializeAsync(new WindowsAzureBlobStorage(client));

            // use folder test-twofolders-checkfilelistofeach1
            // contains comment6..comment9
            // and
            // folder test-twofolders-checkfilelistofeach2
            // contains comment10..comment11
            var _twofoldersCheckfilelistofeach1Folder = await client.SyncContext.GetSyncFolderAsync("test-twofolders-checkfilelistofeach1");
            var _twofoldersCheckfilelistofeach2Folder = await client.SyncContext.GetSyncFolderAsync("test-twofolders-checkfilelistofeach2");

            IEnumerable<ISyncFile> folderFiles1 = ((IEnumerable<ISyncFile>)await _twofoldersCheckfilelistofeach1Folder.ReadAsync());
            IEnumerable<ISyncFile> folderFiles2 = ((IEnumerable<ISyncFile>)await _twofoldersCheckfilelistofeach2Folder.ReadAsync());

            folderFiles1.Should().HaveCount(0);
            folderFiles2.Should().HaveCount(0);
            await _twofoldersCheckfilelistofeach1Folder.PullAsync();

            await _twofoldersCheckfilelistofeach2Folder.PullAsync();

            // Arrange
            folderFiles1 = await _twofoldersCheckfilelistofeach1Folder.ReadAsync();
            folderFiles2 = await _twofoldersCheckfilelistofeach2Folder.ReadAsync();

            // Act

            // Assert
            folderFiles1.Should().HaveCount(4);
            folderFiles2.Should().HaveCount(2);
        }

        private async Task AssertLocalFiles(int num)
        {
            Assert.AreEqual(num, TableHelper.GetCount());

            // Check for SQLLite File
            Assert.AreEqual(1, await BinaryStorageHelper.GetCountAsync());
            // Check for downloaded files
            Assert.AreEqual(num, await BinaryStorageHelper.GetCountAsync(FolderName));

            for (int i = 0; i < num; i++)
            {
                var items = TableHelper.GetItems();
                var file = items.FirstOrDefault(f => f.FileName == string.Format("{0}.txt", i));
                // After push/pull files should have state AsServer
                Assert.AreEqual(SyncFileStatus.AsServer, file.Status);
                Assert.AreEqual(string.Format("{0}.txt", i), file.FileName);

                // Get file and check that it has content
                var localStorageFile =
                    await BinaryStorageHelper.GetFileAsync(
                        string.Format("{0}\\{1}", FolderName, file.FileName));
                Assert.IsNotNull(localStorageFile);
            }
        }
    }
}
